package com.sso.sso.controller;

import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author caoqianhuan
 * @date 2022/8/26
 * @Description: 整个单点登录其实只需要在 拦截器返回时重定向  或者拦截器返回需要重定向标志 前端重定向请求
 */
@Controller
@RequestMapping("/oss")
public class AuthController {
    @Resource
    private TicketCache ticketCache;


    private RestTemplate restTemplate = new RestTemplate();

    private static final String IS_LOGIN = "isLogin";
    /**
     * 考虑并发访问 并发修改的线程问题
     */
    //private String url;
    //@RequestMapping ("/auth/{ticketAndUrl}")
    @RequestMapping("/auth")
    public String authenticate(String ticket, String url, HttpServletRequest request, RedirectAttributes redirectAttributes) {
        //String ticket = request.getParameter("ticket");
        System.out.println(ticket);
        System.out.println(url);
        HttpSession session = request.getSession();
        //验证中心 验证时发现已经登录 重定向回去
        if ((session.getAttribute (IS_LOGIN) != null && (Boolean) session.getAttribute(IS_LOGIN)) || ticketCache.containsTicket(ticket)) {
            System.out.println("hhh好了");
            ticketCache.addUrl(ticket, url);
            //这里要返回已登录信息给源地址  否则 重复拦截重定向
            return "redirect:" + url + "?ticket="+ticket+"&auth=true";
        }

        //使用session作用域传递数据 存在sssion覆盖的问题 如何解决 乐观锁？ 如何不使用session传值 重定向穿值的话需要配合jsp使用
        //session.setAttribute("url", url);
        redirectAttributes.addAttribute("url", url);
        redirectAttributes.addFlashAttribute("url", url);

        System.out.println(redirectAttributes.getAttribute("url"));
        System.out.println(redirectAttributes.getFlashAttributes().get("url"));
        //这里其实可以直接使用ModelAndView返回
        return "redirect:/login.jsp";

    }

    @RequestMapping("/login")
    public String login(String username, String password, String url,
                        HttpSession session, HttpServletRequest request) {
        System.out.println("zzz");
        String ticket = request.getHeader("ticket");
        System.out.println(url);
        if (session.getAttribute(IS_LOGIN) != null && (Boolean) session.getAttribute(IS_LOGIN)) {
            return "redirect:" + url;
        }
        if (ticketCache.containsTicket(ticket)) {
            restTemplate.exchange("http://localhost:9898/oss/auth", HttpMethod.GET,
                    null, String.class, ticket, url);
        }
        System.out.println(username);
        System.out.println(password);
        if (username.equals("cqh")  && password.equals("cqh") ) {
            System.out.println("yse");
            //如果用户重复登录 会产生不同的ticket就麻烦了
            ticket = UUID.randomUUID().toString();
            System.out.println(ticket);
            ticketCache.createTicket(ticket);
            ticketCache.addUrl(ticket, url);
            session.setAttribute(IS_LOGIN, true);
            //重定向ticket作为url参数传递吗  能够放入请求头吗 在前后端分离的场景可以直接
            return "redirect:" + url + "?ticket=" + ticket + "&auth=true";
        } else {
            System.out.println("az");
            return "/login.jsp";
        }
    }

    @RequestMapping("/logoutUrl")
    public void logout(String ticket, HttpSession session) {
        System.out.println("oss logout");
        if (ticketCache.containsTicket(ticket)) {
            Set<String> urls = ticketCache.getUrls(ticket);
            urls.forEach((url) -> {
                System.out.println(url);
                ResponseEntity<Boolean> response = restTemplate.exchange(url+"/realLogout", HttpMethod.GET, null, Boolean.class);
                if (!response.getBody()) {
                    throw new RuntimeException("登出失败");
                }
            });
        }
        session.removeAttribute(IS_LOGIN);
        ticketCache.removeTicket(ticket);
    }
}

@Component
class TicketCache {
    private Map<String, Set<String>> ticketCache = new ConcurrentHashMap<>();

    public Boolean addUrl(String ticket, String url) {
        if (containsTicket(ticket)) {
            ticketCache.get(ticket).add(url);
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    public void createTicket(String ticket) {
        if (!containsTicket(ticket)) {
            ticketCache.put(ticket, new HashSet<>());
        }
    }


    public Boolean containsTicket(String ticket) {
        if (ticket == null) {
            return Boolean.FALSE;
        }
        return ticketCache.containsKey(ticket);
    }

    public void removeTicket(String ticket) {
        ticketCache.remove(ticket);
    }

    public Set<String> getUrls(String ticket) {
        return ticketCache.get(ticket);
    }
}